/**** \file rsrcmanager/rsrcmanager.h
 * \brief Resource Manager
 * \author JD <brossill@enseirb.fr>
 * \date 27.28.04.2007
 */



#ifndef RSRCMANAGER_H
#define RSRCMANAGER_H

#include <unistd.h>

#include "../chainedlib/chainedlist.h"
#include "../global.h"


#define SIZE(br) ((br->end) - (br->begin) + 1)

/// Text concatanate to log
#define RSRCMAN "RSRCMANAGER: "

/// Resource manager save file extension
#define RSRCMAN_SAVE_EXT ".rsrc"

/// Resource manager partial file extension
#define RSRCMAN_PART_EXT ".part"

/// Directory to put parts
#define RSRCMAN_DIRECTORY_PARTS "./parts/"
#define RSRCMAN_PERM_PARTS 0755

/// Directory to put saves
#define RSRCMAN_DIRECTORY_SAVES "./saves/"
#define RSRCMAN_PERM_SAVES 0755

/// Directory to put complete files
#define RSRCMAN_DIRECTORY_INCOMING "./incoming/"
#define RSRCMAN_PERM_INCOMING 0755

/** Some default values */
#define DEFAULT_PERM 0644

#define DEFAULT_BLOCK_SIZE 4095

/** Define the block status information container*/
typedef char blockstatus_t;

///No data in the block
#define BSTAT_NONE 0

///No data in the block but a write is highly probable in a few
#define BSTAT_POSTPONED 1

///The block is currently written or reserved for writing
#define BSTAT_WRITING 2

///The block got payload
#define BSTAT_LOADED 3

///The block got payload and it's check
#define BSTAT_CHECK 4

///The block is used in RMC, it means
/// that the block is not sent to the father.
#define BSTAT_RMC_NONE_SENT 100

///The block is used in RMC, it means
/// that the block is sent to the father.
#define BSTAT_RMC_SENT 101


/** Structure associated to a local ressource */
typedef struct _localResource_s
{
  /// Chained list containing the blocks status mapping
  CLNode_s* status;

  /// Resource key
  filekey_t key;

  /// Name of the file on disk
  char* filename;
  
  /// file size (in blocks)
  size_t size;

  /// block size (in bytes)
  size_t block_size;

  /// Last block size (this block got it special size bit set)
  size_t last_block_size;

}localResource_s;

/// Structure associated to a block range status
typedef struct _blockRange_s
{
  /// First block of the range
  off_t begin;

  /// Last block
  off_t end;

  /// block range status
  blockstatus_t bstatus;

  /// child id link to the bloc status
  pid_t pid;

}blockRange_s;


/** \brief Create a new local resource
  * \param name resource filename
  * \param key a 16bits key
  * \param size file size (in bytes)
  */
localResource_s* RM_create_local_resource(char* name,
                                           filekey_t key,
                                           size_t size);

/** \brief Destroy a local resource
  * \param lr reference to the local resource to destroy
  * \warning This function DOESN'T REMOVE FILE FROM
  *           HARDDRIVE !
*/
void RM_destroy_local_resource( localResource_s* lr );

/** \brief Settle down resource manager.
  * \return 0 in case of succes, nonzero in other cases
  * This function must be called at the beginning
  * of the program to set properly directories and
  * permissions.
  */
char RM_settle_down( void );


char RM_check_dir( char* path, int perms );

/** \brief Set state of a block range in a local resource
  * \param lr reference to a local resource
  * \param status new block status
  * \param first first block of the range
  * \param last last block of the range
  * \return reference to the allocated block
  * \warning The reference to the allocated block
  *returned by this function may not be no longer 
  *valid after RM_stick_adjacent !
*/
blockRange_s* RM_set_range_state(localResource_s* lr,
                        blockstatus_t status,
                        pid_t pid,
                        off_t first,
                        off_t last);

/**\brief Same as RM_set_range_state but take a list node.
  */
blockRange_s* _RM_set_range_state(CLNode_s* root,
                        size_t size,
                        blockstatus_t status,
                        pid_t pid,
                        off_t first,
                        off_t last);


/** \brief Recalculate and stick together adjacent blocs
            with same properties
*/
void RM_stick_adjacent(localResource_s* lr);

/** \brief Same as RM_stick_adjacent but with list node
*/
void _RM_stick_adjacent(CLNode_s*);


/** \brief Find and return a matching range according
  *the fmatch function  included in [first,last].
  * \param lr reference to a local resource
  * \param fmatching reference to a function which
  *whill say if, depending of the block range, the pid,
  *the state, the block is interesting.
  * \param br reference to a block range to return
  *   the search result.
  * \param max_state maximum state level
  * \param first bloc of the search range.
  * \param last bloc of the search range.
  * \param pid pid of the requesting child.
  * \return the size of the found block range in blocks.
  * \warning The function doesn't allocate or change
  * zone status ! Use RM_reserve_block_range or
  * RM_lock_write_block_range instead
  */
size_t RM_return_matching_range(localResource_s* lr,
 char (*fmatching)(int state, blockRange_s* br, pid_t pid),
                            int max_state,
                            blockRange_s* br,
                            off_t first,
                            off_t last,
                            pid_t pid);

/** \brief Find and reserve a block range for a child.
  * \param first first bloc of the search range.
  * \param last  last  bloc of the search range.
  * \param pid pid of the requesting child.
  * \return The function return a reference to the new allocated block range.
  * 
  * The function will reserve a block range in [first,last]
  * range.
  * The block range will follow the rules :
 */
blockRange_s* RM_reserve_block_range(localResource_s* lr,
                             off_t first,
                             off_t last,
                             pid_t pid);

/** \brief Find and lock for writing a block range
  * in [first,last] with a maximum size of size.
  * \param lr reference to a local resource.
  * \param first block of the range.
  * \param last block of the range.
  * \param size maximum size of the block (0 for no limit).
  * \param pid pid of the requesting child.
  * \return reference to the allocated block.
  * \warning The reference to the allocated block
  *returned by this function may not be no longer 
  *valid after RM_stick_adjacent !
  *
  * RM_lock_write_block_range use RM_return_available_range
  * to find a block range. If RM_return_avaible_range return
  * a block range longer that size the block will be 
  * truncated by the right and locked for writing. (WRITING)
  *
*/
blockRange_s* RM_lock_write_block_range(localResource_s* lr,
                                        off_t first,
                                        off_t last,
                                        size_t size,
                                        pid_t pid);


/**\brief Save the local resource to disk.
  *\param lr reference to local resource.
  *\return 0 on success
  */
char RM_local_resource_to_disk(localResource_s* lr);

/**\brief Load the local resource from disk.
  *\warning destroy all data in the local resource before
          writing the new one from disk.
  *\param lr reference to local resource.
  *\param name on the resource to load.
  *\return 0 on success
*/
char RM_local_resource_from_disk(localResource_s* lr,
                                  char* name);


/***********************************************************/
/***********************************************************/

typedef struct _range_s
{
  off_t begin;

  off_t end;

}range_s;

typedef struct _partialResource_s
{
  /// Chained list containing the blocks status mapping
  CLNode_s* status;

  /// fd of the resource part file
  int fd;

  /// Resource key
  filekey_t key;
 
  /// Size of the partial block
  size_t size;

  /// Block size
  size_t block_size;

  /// Current offset
  off_t current;

}partialResource_s;

/***********************************************************/


/**\brief Create a partial resource for the FTAL childrens.
  *\param fkey Unique file ID.
  *\param start First block of the write range
  *\param end Last block of the write range
  *\param block_size Block size of the local resource
  *\return reference to the created partial resource
  */
partialResource_s* RMC_create_partial_resource(filekey_t fkey,
                                           off_t start,
                                           off_t end,
                                           size_t block_size);

/**\brief Destroy a partial resource.
  *\param pr partial resource to wipe out
  */
void RMC_destroy_partial_resource( partialResource_s* pr);

/**\brief Write 'size' data to the resource right
  *after the previous write.
  *\param pr file partial resource  
  *\param data reference to the data which will be written
  *\param size size of the data in bytes
  *\return written data size and {-1,-1} in case of error
  */
size_t RMC_write( partialResource_s* pr,
                    void* data,
                    size_t size );

/**\brief Write 'size' data to the resource right
  *after the given offset.
  *\param pr file partial resource  
  *\param data reference to the data which will be written
  *\param size size of the data in bytes
  *\param offset write offset in bytes
  *\return written data size and {-1,-1} in case of error
  */
size_t RMC_write_offset( partialResource_s* pr,
                          void* data,
                          size_t size,
                          off_t offset );

/**\brief Read 'size' data at 'offset'
  *\param pr reference to a partial resource
  *\param data reference where the data will be placed
  *\param size size to read in bytes
  *\param offset offset in the file in bytes
  *\return length of the read data
  */
size_t RMC_read( partialResource_s* pr,
                  void* data, 
                  size_t size,
                  off_t offset);

/**\brief Return a valid 'none sent' range (checked written)
  * and change the range to 'sent'.
  *\param pr reference to the partial resource.
  *\return Valid area range in blocks
  */
range_s RMC_get_valid_range( partialResource_s* pr);

/***********************************************************/
/***********************************************************/


/** \brief Test function made to feed RM_return_matching_range
  *
  * The function will return true if the block range 
  * follow the rules (number before are priority levels/state)
  *
  *  1. If a NONE block range exist in the range 
  *(no matter the pid), RM will return the largest one.
  *
  *  2. If no block is found the function will return 0.
*/
char fmatching_postponed( int state, blockRange_s* br, pid_t pid);

/** \brief Test function made to feed RM_return_matching_range
  * The function will return true if the block range 
  * follow the rules (number before are priority levels/state)
  *
  * 1. If a POSTPONED block range exist in the range (with
  *same pid), RM will return the largest one.
  *
  * 2. If a NONE block range exist in the range (no matter
  *the pid), RM will return the largest one.
  *
  * 3. If a POSTPONED block range exist in the range (no
  *matter the pid), RM will return the largest one.
  *
  * 4. If no block is found the function will return 0.
  *
*/
char fmatching_writing( int state, blockRange_s* br, pid_t pid);


char fmatching_valid( int state, blockRange_s* br,
                        pid_t pid);

#endif/*RSRCMANAGER_H*/
